﻿using System;
using System.Drawing;
using System.Windows.Forms;
using CamOnWebController.Helper;
using CamOnWebController.Properties;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Threading;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace CamOnWebController
{
    /// <summary>
    /// WebCam control class.
    /// </summary>
    [ToolboxBitmap(typeof(CamOnWebComponent), "logoComp.bmp")]
    public partial class CamOnWebComponent : UserControl, IDisposable
    {
        AviHelper avi = new AviHelper();
        Bitmap bmp = null;

        // Height and width of the image generated by the WebCam.
        private int m_Width = 500;
        private int m_Height = 500;

        // Interval Frame Rate.
        private int intervalFrameRate = 50;

        // Array of items in the combo driver.
        private static string[] lstItems;

        // Handle of the control window of the webcam.
        private int mCapHwnd;

        // Flag to check if webcam was stopped.
        private bool bStopped = true;

        // Responsible for indexing video driver.
        private static short driverVideoIndex;

        /// <summary>
        ///Property responsible for selecting the driver if there are more than one computer.
        /// </summary>
        public short DriverVideoIndex
        {
            get
            {
                return driverVideoIndex;
            }
            set
            {
                driverVideoIndex = value;
            }
        }

        /// <summary>
        /// Driver list.
        /// </summary>
        public string[] Drivers
        {
            get
            {
                return CamOnWebComponent.lstItems;
            }
        }

        /// <summary>
        /// Property for image manipulation.
        /// </summary>
        public PictureBox Image
        {
            get
            {
                return this.ImgWebCamShow;
            }

            set
            {
                this.ImgWebCamShow = value;
            }
        }

        /// <summary>
        /// Class Destructor.
        /// </summary>
        ~CamOnWebComponent()
        {
            this.Stop();
        }

        /// <summary>
        /// Class Constructor.
        /// </summary>
        public CamOnWebComponent()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Event creation of control.
        /// </summary>
        protected override void OnCreateControl()
        {
            base.OnCreateControl();
            if (!this.DesignMode)
            {
                this.ImgWebCamShow.Image = Resources.CamOnWeb;
                pnMenu.Left = -166;
                lblBorda.Text = ">>";
                LoadDriversList();
            }
        }

        /// <summary>
        /// Method for take pictures.
        /// </summary>
        public void TakePicture()
        {
            BitmapHelper bmp = new BitmapHelper();

            SaveFileDialog saveDialog = new SaveFileDialog();

            saveDialog.Filter = "Bitmap (*.bmp)|*.bmp|Jpeg (*.jpg)|*.jpg|PNG (*.png)|*.png";

            saveDialog.CheckFileExists = false;
            saveDialog.InitialDirectory = Application.StartupPath;

            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                Image img = (Image)ImgWebCamShow.Image.Clone();

                if (saveDialog.FileName.IndexOf(".bmp") > 0)
                {
                    bmp.SaveImage(img, saveDialog.FileName, ImageFormat.Bmp);
                }
                else if (saveDialog.FileName.IndexOf(".jpg") > 0)
                {
                    bmp.SaveImage(img, saveDialog.FileName, ImageFormat.Jpeg);
                }
                else if (saveDialog.FileName.IndexOf(".png") > 0)
                {
                    bmp.SaveImage(img, saveDialog.FileName, ImageFormat.Png);
                }
            }
        }

        /// <summary>
        /// Method for take pictures.
        /// </summary>
        public void TakePicture(string pathFileSave)
        {
            BitmapHelper bmp = new BitmapHelper();

            Image img = (Image)ImgWebCamShow.Image.Clone();

            if (pathFileSave.IndexOf(".bmp") > 0)
            {
                bmp.SaveImage(img, pathFileSave, ImageFormat.Bmp);
            }
            else if (pathFileSave.IndexOf(".jpg") > 0)
            {
                bmp.SaveImage(img, pathFileSave, ImageFormat.Jpeg);
            }
            else if (pathFileSave.IndexOf(".png") > 0)
            {
                bmp.SaveImage(img, pathFileSave, ImageFormat.Png);
            }
        }

        public int IntervalFrameRate
        {
            get { return intervalFrameRate; }
            set { intervalFrameRate = value; }
        }

        /// <summary>
        /// Adjusts the size of the WebCam image with the screen size.
        /// </summary>
        private void SetImageSize()
        {
            this.m_Width = this.ImgWebCamShow.Size.Width;
            this.m_Height = this.ImgWebCamShow.Size.Height;
        }

        /// <summary>
        /// Starts the WebCam screen capture.
        /// </summary>
        public void Start()
        {
            try
            {
                // Adjust the image size.
                this.SetImageSize();

                // For security, call the stop method just to make sure we're not running the code.
                this.Stop();

                this.InitializeDrivers();

                // We set the capture interval webcam. 
                // Here we create a property of the component to change the time, 
                // remembering that the larger the longer the delay 
                // between the captured and displayed.
                this.tmrRefrashFrame.Interval = intervalFrameRate;

                this.bStopped = false;
                this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("An error occurred when viewing the WebCam. Make sure everything is connected.\r\n\n" + excep.Message);
                this.Stop();
            }
        }

        /// <summary>
        /// Initilizes the video recording.
        /// </summary>
        public void RecordVideo()
        {
            SaveFileDialog saveDialog = new SaveFileDialog();

            saveDialog.Filter = "AVI Video (*.avi)|*.avi";

            saveDialog.CheckFileExists = false;
            saveDialog.InitialDirectory = Application.StartupPath;

            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                bmp = avi.Open(saveDialog.FileName, 13, ImgWebCamShow.Image.Width, ImgWebCamShow.Image.Height);
            }
        }

        /// <summary>
        /// Stops the video recording.
        /// </summary>
        public void StopVideoRecord()
        {
            bmp = null;
            avi.Close();
        }

        /// <summary>
        /// Initializes Webcam Drivers.
        /// </summary>
        public void InitializeDrivers()
        {
            // Creates the capture window by using the API "capCreateCaptureWindowA".
            mCapHwnd = User32.capCreateCaptureWindowA("WebCap", 0, 0, 0, this.m_Width, this.m_Height, ImgWebCamShow.Handle.ToInt32(), 0);

            // Release resources for the operating system.
            Application.DoEvents();

            // Starts the thread to load the correct drivers.
            Thread t = new Thread(LoadCorrectDriver);
            t.Start();

            // Sends a message via the OS to connect to the webcam driver.
            if (User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_CONNECT, 0, 0) == 1)
            {
                User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_SET_SCALE, 1, 0);
                User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_SET_PREVIEWRATE, 30, 0);
                User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_SET_PREVIEW, 1, 0);
            }
            else
            {
                this.Stop();
            }
        }

        public void LoadDriversList()
        {
            try
            {
                // Creates the capture window by using the API "capCreateCaptureWindowA".
                mCapHwnd = User32.capCreateCaptureWindowA("WebCap", 0, 0, 0, this.m_Width, this.m_Height, this.Handle.ToInt32(), 0);

                // Release resources for the operating system.
                Application.DoEvents();

                // Starts the thread to load the correct drivers.
                Thread t = new Thread(LoadCorrectDriver);
                t.Start();

                // Sends a message via the OS to connect to the webcam driver.
                User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_CONNECT, 0, 0);

                this.Stop();

                cboDriver.Items.Clear();

                foreach (string item in lstItems)
                {
                    cboDriver.Items.Add(item);
                }
            }
            catch
            {
            }
        }

        /// <summary>
        /// Method to update the driver if there are more video drivers installed in Windows.
        /// </summary>
        private static void LoadCorrectDriver()
        {
            Stopwatch sw = new Stopwatch();
            IntPtr x = IntPtr.Zero;
            IntPtr y = IntPtr.Zero;
            IntPtr z = IntPtr.Zero;

            sw.Start();

            // Wait while Hadler is found.
            do
            {
                x = User32.FindWindow("#32770", "Video Source");

                User32.ShowWindow(x, ShowWindowCommands.Hide);

                y = User32.FindWindowEx(x, IntPtr.Zero, "#32770", "Capture Source");
                z = User32.FindWindowEx(y, IntPtr.Zero, "ComboBox", IntPtr.Zero);

                if (sw.Elapsed.Seconds > 2)
                {
                    sw.Stop();
                    return;
                }
            }
            while (x == IntPtr.Zero || y == IntPtr.Zero || z == IntPtr.Zero);

            sw.Stop();

            // Retrieves the amount of items in the combobox.
            int count = User32.SendMessage(z, User32.CB_GETCOUNT, 0, 0);

            lstItems = new string[count];

            // Adds the names of video capture drives in the array.
            for (int i = 0; i < count; i++)
            {
                StringBuilder ssb = new StringBuilder(256, 256);

                User32.SendRefMessage(z, User32.CB_GETLBTEXT, i, ssb);

                lstItems[i] = ssb.ToString();
            }

            LoadConfig();

            // Select a particular driver.
            User32.SendMessage(z, User32.CB_SETCURSEL, driverVideoIndex, 0);

            // Retrieves the handle of the OK button.
            IntPtr btnOK = User32.FindWindowEx(x, IntPtr.Zero, "Button", "OK");

            // Sends message LBUTTONDOWN.
            long lngResult = User32.SendMessage(btnOK, User32.WM_LBUTTONDOWN, 0, 0);

            // Sends message LBUTTONUP.
            lngResult = User32.SendMessage(btnOK, User32.WM_LBUTTONUP, 0, 0);
        }


        /// <summary>
        /// Method to stop the display of the webcam.
        /// </summary>
        public void Stop()
        {
            try
            {
                // stop the timer
                bStopped = true;
                this.tmrRefrashFrame.Stop();

                // Release resources for the operating system.
                Application.DoEvents();

                // Send message to SO to disconnect the webcam.
                User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_DISCONNECT, 0, 0);

                // Destroys the window.
                User32.DestroyWindow(new IntPtr(this.mCapHwnd));
            }
            catch
            { // Does not trigger any errors.
            }
        }

        /// <summary>
        /// Capture frames.
        /// </summary>
        private void tmrRefrashFrame_Tick(object sender, EventArgs e)
        {
            try
            {
                // Pause the timer.
                this.tmrRefrashFrame.Stop();

                // Adjust the image size.
                this.SetImageSize();

                ProcessarImagemClipboard();

                //Refresh image.
                ImgWebCamShow.Refresh();

                // Release resources for the operating system.
                Application.DoEvents();

                if (!bStopped)
                {
                    this.tmrRefrashFrame.Start();
                }
            }
            catch (Exception excep)
            {
                MessageBox.Show("An error occurred when viewing the WebCam. Make sure everything is connected.\r\n\n" + excep.Message);
                this.Stop(); // stop the process
            }
        }

        /// <summary>
        /// Method to retrieve the image from webcam using clipboard.
        /// </summary>
        private void ProcessarImagemClipboard()
        {
            // Send the message to the OS to capture the next frame.
            User32.SendMessage(new IntPtr(mCapHwnd), User32.WM_CAP_GET_FRAME, 0, 0);

            // Copy the captured frame to the clipboard.
            User32.SendMessage(new IntPtr(mCapHwnd), User32.WM_CAP_COPY, 0, 0);

            // We display the frame of the webcam in control.
            ImgWebCamShow.Image = ClipboardHelper.GetImage();

            if (bmp != null)
            {
                Graphics g = Graphics.FromImage(bmp);

                g.TranslateTransform((float)ImgWebCamShow.Image.Width / 2, (float)ImgWebCamShow.Image.Height / 2);
                g.RotateTransform(180);
                g.TranslateTransform(-(float)ImgWebCamShow.Image.Width / 2, -(float)ImgWebCamShow.Image.Height / 2);

                g.DrawImage(ImgWebCamShow.Image, new Point(0, 0));
                avi.AddFrame();
            }
        }

        /// <summary>
        /// Implementation of the dispose method.
        /// </summary>
        void IDisposable.Dispose()
        {
            // Send message to the OS to disconnect the webcam.
            User32.SendMessage(new IntPtr(this.mCapHwnd), User32.WM_CAP_DISCONNECT, 0, 0);

            // Destroy Window.
            User32.DestroyWindow(new IntPtr(this.mCapHwnd));

            this.tmrRefrashFrame.Stop();

            this.Dispose();
        }

        private void CamOnWebComponent_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Loads the driver configuration.
        /// </summary>
        private static void LoadConfig()
        {
            try
            {
                using (StreamReader sr = new StreamReader(Application.StartupPath + @"\c.o"))
                {
                    string x = sr.ReadToEnd();
                    driverVideoIndex = Convert.ToInt16(x);
                }
            }
            catch
            {
                driverVideoIndex = 0;
            }
        }

        /// <summary>
        /// Expands / Contract the menu.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lblBorda_Click(object sender, EventArgs e)
        {
            if (lblBorda.Text.Equals(">>"))
            {
                pnMenu.Left = 0;
                lblBorda.Text = "<<";
            }
            else
            {
                pnMenu.Left = -166;
                lblBorda.Text = ">>";
            }
        }

        /// <summary>
        /// Saves the driver config.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSaveConfig_Click(object sender, EventArgs e)
        {
            using (StreamWriter outfile = new StreamWriter(Application.StartupPath + @"\c.o"))
            {
                outfile.Write(cboDriver.SelectedIndex.ToString());
                MessageBox.Show("Restart the application to work properly!");
            }
        }
    }
}